﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace BCCommon
{
    /// <summary>
    /// 执行方法（反射）
    /// </summary>
    public static class ExecuteMethodUtils
    {
        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="serviceName">Service名称</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="arguments">参数列表</param>
        /// <returns></returns>
        public static object ExecuteMethod(this IServiceProvider serviceProvider, string serviceName, string methodName, params object[] arguments)
        {
            //开始构建动态服务
            Type type = Type.GetType(serviceName);

            //获取构造参数列表最多的构造方法
            ConstructorInfo firstConstructor = type.GetTypeInfo().GetConstructors().FirstOrDefault();

            foreach (var constructor in type.GetTypeInfo().GetConstructors())
            {
                if (constructor.GetParameters().Length > firstConstructor.GetParameters().Length)
                {
                    firstConstructor = constructor;
                }
            }

            //提取构造参数
            var constructorParameters = firstConstructor.GetParameters().Select(p => serviceProvider.GetService(p.ParameterType)).ToArray();

            //构建服务实例
            var service = Activator.CreateInstance(type, constructorParameters);

            //构建服务方法
            var method = type.GetMethod(methodName);

            //提取服务方法调用参数列表
            var methodParameters = method.GetParameters();

            if (methodParameters.Length != arguments.Length)
            {
                throw new ArgumentException("形参与实参不相等。");
            }

            var parameterValues = new List<object>();

            for (int i = 0; i < arguments.Length; i++)
            {
                parameterValues.Add(Convert.ChangeType(arguments[i], methodParameters[i].ParameterType));
            }

            return method.Invoke(service, parameterValues.ToArray());
        }

        public static TResult ExecuteMethod<TResult>(this IServiceProvider serviceProvider, string serviceName, string methodName, params object[] arguments)
        {
            var result = serviceProvider.ExecuteMethod(serviceName, methodName, arguments);

            if (result is TResult)
            {
                return (TResult)result;
            }

            throw new InvalidOperationException("当前方法的结果与预期不符。");
        }
    }
}
